package com.linln.admin.system.controller;

import com.linln.admin.meeting.service.DistributeLockComponent;
import com.linln.common.config.properties.ProjectProperties;
import com.linln.common.data.URL;
import com.linln.common.enums.ResultEnum;
import com.linln.common.enums.StatusEnum;
import com.linln.common.exception.ResultException;
import com.linln.common.utils.CaptchaUtil;
import com.linln.common.utils.ResultVoUtil;
import com.linln.common.utils.SpringContextUtil;
import com.linln.common.vo.ResultVo;
import com.linln.component.actionLog.action.UserAction;
import com.linln.component.actionLog.annotation.ActionLog;
import com.linln.component.shiro.ShiroUtil;
import com.linln.modules.system.domain.Role;
import com.linln.modules.system.domain.User;
import com.linln.modules.system.service.DeptService;
import com.linln.modules.system.service.RoleService;
import com.linln.modules.system.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * @author 小懒虫
 * @date 2018/8/14
 */
@Controller
public class LoginController implements ErrorController {
    public static final long NORMAL_ROLE = 3L;

    @Value("${oauth2.client.response.logoutUrl:/sso/logout}")
    private String oauthLogoutUrl;
    @Value("${com.beta.oauth2.login.enable:false}")
    private boolean oauthEnable;

    @Autowired
    private RoleService roleService;
    @Autowired
    private UserService userService;
    @Autowired
    private DeptService deptService;
    @Autowired
    private DistributeLockComponent lockComponent;

    /**
     * 跳转到登录页面
     */
    @GetMapping("/login")
    public String toLogin(Model model) {
        ProjectProperties properties = SpringContextUtil.getBean(ProjectProperties.class);
        model.addAttribute("isCaptcha", properties.isCaptchaOpen());
        return "/login";
    }

    /**
     * 实现登录
     */
    @PostMapping("/login")
    @ResponseBody
    @ActionLog(key = UserAction.USER_LOGIN, action = UserAction.class)
    public ResultVo login(String username, String password, String captcha, String rememberMe) {
        // 判断账号密码是否为空
        if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
            throw new ResultException(ResultEnum.USER_NAME_PWD_NULL);
        }

        // 判断验证码是否正确
        ProjectProperties properties = SpringContextUtil.getBean(ProjectProperties.class);
        if (properties.isCaptchaOpen()) {
            Session session = SecurityUtils.getSubject().getSession();
            String sessionCaptcha = (String) session.getAttribute("captcha");
            if (StringUtils.isEmpty(captcha) || StringUtils.isEmpty(sessionCaptcha)
                    || !captcha.toUpperCase().equals(sessionCaptcha.toUpperCase())) {
                throw new ResultException(ResultEnum.USER_CAPTCHA_ERROR);
            }
            session.removeAttribute("captcha");
        }

        // 1.获取Subject主体对象
        Subject subject = SecurityUtils.getSubject();

        // 2.封装用户数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);

        // 3.执行登录，进入自定义Realm类中
        try {
            // 判断是否自动登录
            if (rememberMe != null) {
                token.setRememberMe(true);
            } else {
                token.setRememberMe(false);
            }
            subject.login(token);

            // 判断是否拥有后台角色
            User user = ShiroUtil.getSubject();
            if (roleService.existsUserOk(user.getId())) {// 不同的角色的用户进入不同的界面
                if (ShiroUtil.getSubjectRoles().size() == 1 && ShiroUtil.getSubjectRoles().stream().findFirst().get().getId().longValue() == NORMAL_ROLE) {
                    return ResultVoUtil.success("登录成功", new URL("/meeting/order/dashboard"));
                } else {
                    return ResultVoUtil.success("登录成功", new URL("/#/meeting/order/dashboard"));
                }
            } else {
                SecurityUtils.getSubject().logout();
                return ResultVoUtil.error("您没有被分配角色，没有权限访问系统！");
            }
        } catch (LockedAccountException e) {
            return ResultVoUtil.error("该账号已被冻结");
        } catch (AuthenticationException e) {
            return ResultVoUtil.error("用户名或密码错误");
        }
    }

    /**
     * 验证码图片
     */
    @GetMapping("/captcha")
    public void captcha(HttpServletRequest request, HttpServletResponse response) throws IOException {
        //设置响应头信息，通知浏览器不要缓存
        response.setHeader("Expires", "-1");
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Pragma", "-1");
        response.setContentType("image/jpeg");

        // 获取验证码
        String code = CaptchaUtil.getRandomCode();
        // 将验证码输入到session中，用来验证
        request.getSession().setAttribute("captcha", code);
        // 输出到web页面
        ImageIO.write(CaptchaUtil.genCaptcha(code), "jpg", response.getOutputStream());
    }

    /**
     * 退出登录
     */
    @GetMapping("/logout")
    public String logout() {
        SecurityUtils.getSubject().logout();
        // 把服务端也退出登录
        return "redirect:" + StringUtils.defaultIfBlank(oauthEnable ? oauthLogoutUrl : "/", "/");
    }

    /**
     * 权限不足页面
     */
    @GetMapping("/noAuth")
    public String noAuth() {
        return "/system/main/noAuth";
    }

    /**
     * 自定义错误页面
     */
    @Override
    public String getErrorPath() {
        return "/error";
    }

    /**
     * 处理错误页面
     */
    @RequestMapping("/error")
    public String handleError(Model model, HttpServletRequest request) {
        Integer statusCode = (Integer) request.getAttribute("javax.servlet.error.status_code");
        String errorMsg = "好像出错了呢！";
        if (statusCode == 404) {
            errorMsg = "页面找不到了！好像是去火星了~";
        }

        model.addAttribute("statusCode", statusCode);
        model.addAttribute("msg", errorMsg);
        return "/system/main/error";
    }


    /**
     * 跳转到注册页面
     */
    @GetMapping("/reg")
    public String reg(Model model) {
        return "/reg";
    }

    /**
     * 实现登录
     */
    @PostMapping("/reg")
    @ResponseBody
    public ResultVo reg(User user, String confirmpassword) {
        if (user != null) {
            return ResultVoUtil.error("注册功能已关闭！");
        }
        if (StringUtils.isBlank(user.getUsername())) {
            return ResultVoUtil.error("请输入登录名！");
        }
        if (StringUtils.isBlank(user.getNickname())) {
            return ResultVoUtil.error("请输入昵称！");
        }

        if (StringUtils.isBlank(user.getPassword())) {
            return ResultVoUtil.error("请输入密码！");
        }

        if (!StringUtils.equals(user.getPassword(), confirmpassword)) {
            return ResultVoUtil.error("两次输入的密码不一致！");
        }

        String lockey = "reg=" + user.getUsername();
        if (lockComponent.tryLock(lockey)) {
            try {
                User u = new User();
                u.setUsername(user.getUsername());
                u.setStatus(StatusEnum.OK.getCode());

                if (userService.getPageList(u).hasContent()) {
                    return ResultVoUtil.error("用户名已经存在，请换一个！");
                }

                String salt = ShiroUtil.getRandomSalt();
                String encrypt = ShiroUtil.encrypt(user.getPassword(), salt);
                user.setPassword(encrypt);
                user.setSalt(salt);

                user.setDept(deptService.getById(1L));
                user.setCreateDate(new Date());
                user.setEmail("");
                user.setSex((byte) 1);
                user.setStatus(StatusEnum.OK.getCode());
                user.setUpdateDate(new Date());
                userService.save(user);

                //授权为默认值
                Set<Role> roles = new HashSet<>();
                roles.add(roleService.getById(NORMAL_ROLE));
                user.setRoles(roles);
                userService.save(user);
            } finally {
                lockComponent.releaseLock(lockey);
            }
        }
        return ResultVoUtil.success("注册成功！");
    }
}
